/********************************************************
*Implementacion de Tabla de simbolos                    *
*Author     :   Simon Emmanuel Gutierrez Brida          *
*Version    :   0.1                                     *
*Fecha      :   Septiembre/2011                         *
*------------------------------------------------------ *
*********************************************************/

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "tablasimbolos.h"
#include "../lista/lista.h"

TablaSimbolos* nuevaTabla() {
    TablaSimbolos *nuevo = (TablaSimbolos*) malloc(sizeof(TablaSimbolos));
    nuevo->tablaSimbolos = nuevaListaListas();
    nuevo->nivelActual = 0;
    agregar_final_listaListas(nuevo->tablaSimbolos, NULL);
    return nuevo;
}

Variable* obtenerVariable(TablaSimbolos *tabla, int nivel, char *id) {
    bool validLevel = (nivel >= 0) && (nivel < longitud_listaListas(tabla->tablaSimbolos));
    if (validLevel) {
        Lista *listaNivel = obtener_listaListas(tabla->tablaSimbolos, nivel);
        if (listaNivel != NULL) {
            return obtener(listaNivel, id);
        } else {
            return NULL;
        }
    } 
}

void mostrarTabla(TablaSimbolos *tabla) {
    int nivel = nivelActual(tabla);
    while (nivel >= 0) {
        Lista *actual = obtener_listaListas(tabla->tablaSimbolos, nivel);
        printf("elementos en nivel %d\n", nivel);
        if (actual != NULL) {
            int i = longitud(actual) - 1;
            printf("hay %d elementos\n", i + 1);
            while (i >= 0) {
                Variable *var = obtener_indexado(actual, i);
                if (var != NULL) {
                    printf("var : %s ; tipo : %d\n", obtenerIdentificador(var), obtenerTipo(var));
                } else {
                    printf("NULL\n");
                }
                i--;
            }
        } else {
            printf("ninguno\n");
        }
        nivel--;
    }
}

Variable* obtenerMasCercana(TablaSimbolos *tabla, char *id) {
    int nivelActual = tabla->nivelActual;
    bool found = false;
    Variable *resultado = NULL;
    while ((nivelActual >= 0) && (!found)) {
        Lista *actual = obtener_listaListas(tabla->tablaSimbolos, nivelActual);
        if (actual != NULL) {
            resultado = obtener(actual, id);
            if (resultado != NULL) {
                found = true;
            } else {
                nivelActual--;
            }
        } else {
            nivelActual--;
        }
    }
    return resultado;
}



bool existe(TablaSimbolos *tabla, char *id) {
    int i = 0;
    bool found = false;
    while ((i < longitud_listaListas(tabla->tablaSimbolos)) && (!found)) {
        found = existeEnNivel(tabla, i, id);
        if (!found) {
            i++;
        }
    }
    return found;
}

bool existeEnNivelActual(TablaSimbolos *tabla, char *id) {
    return existeEnNivel(tabla, nivelActual(tabla), id);
}

bool existeEnNivel(TablaSimbolos *tabla, int nivel, char *id) {
    return obtenerVariable(tabla, nivel, id) != NULL;
}

int nivelActual(TablaSimbolos *tabla) {
    return tabla->nivelActual;
}

void abrirNivel(TablaSimbolos *tabla) {
    tabla->nivelActual = nivelActual(tabla) + 1;
    if (longitud_listaListas(tabla->tablaSimbolos) < (tabla->nivelActual + 1)) {
        agregar_final_listaListas(tabla->tablaSimbolos, NULL);
    }
}

void cerrarNivel(TablaSimbolos *tabla) {
    borrar_listaListas(tabla->tablaSimbolos, tabla->nivelActual);
    tabla->nivelActual = nivelActual(tabla) - 1;
}

bool insertarVariable(TablaSimbolos *tabla, Variable *var) {
    bool existe = existeEnNivelActual(tabla, obtenerIdentificador(var));
    if (!existe) {
        Lista *nivelActual_lista = obtener_listaListas(tabla->tablaSimbolos, nivelActual(tabla));
        if (nivelActual_lista == NULL) {
            nivelActual_lista = nuevaLista();
            agregar(nivelActual_lista, var);
            reescribir_listaListas(tabla->tablaSimbolos, nivelActual_lista, tabla->nivelActual);
        } else {
            agregar(nivelActual_lista, var);
        }
    }
    return (!existe);
}

bool eliminarVariable_id(TablaSimbolos *tabla, char *id) {
    bool existe = existeEnNivelActual(tabla, id);
    if (existe) {
        Lista *nivelActual = obtener_listaListas(tabla->tablaSimbolos, tabla->nivelActual);
        eliminar(nivelActual, id);
        if (longitud(nivelActual) == 0) {
            borrar_listaListas(tabla->tablaSimbolos, tabla->nivelActual);
        }
    }
    return existe;
}

bool eliminarVariable_var(TablaSimbolos *tabla, Variable *var) {
    return eliminarVariable_id(tabla, obtenerIdentificador(var));
}